cmake_minimum_required(VERSION 3.15)
project(txdnn)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(GLOBAL_OPTIONS ${CMAKE_CXX_FLAGS}
    -fPIC
)
set(GLOBAL_LINKS "pthread")
set(base_out_dir pytxdnn)
set(output_bin_dir ${base_out_dir}/bin)
set(output_lib_dir ${base_out_dir}/lib)

option(ASAN "asan" OFF)
if (ASAN)
    set(GLOBAL_OPTIONS -fsanitize=address ${GLOBAL_OPTIONS})
    set(GLOBAL_LINKS asan ${GLOBAL_LINKS})
    set(CMAKE_BUILD_TYPE Debug)
endif ()

if(NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release)
endif()

if(CMAKE_BUILD_TYPE STREQUAL Debug)
    set(GLOBAL_OPTIONS ${GLOBAL_OPTIONS}
        -g
        -ggdb
        -O0
    )
else()
    set(GLOBAL_OPTIONS ${GLOBAL_OPTIONS}
        -O3
    )
    if (ASAN)
        message(FATAL_ERROR "Enable ASan must also enable Debug, param:-DCMAKE_BUILD_TYPE=Debug")
    endif (ASAN)
endif()

message(STATUS "ASAN:${ASAN}")
message(STATUS "CMAKE_BUILD_TYPE:${CMAKE_BUILD_TYPE}")

#########   macro & function: git clone, rev-parse --short, checkout    #########
# git clone repository
macro(git_clone repo_address dst_dir)
    find_package(Git QUIET)
    if(GIT_FOUND)
        message(STATUS "${repo_address}, cloning ...")
        execute_process(
            # # submodule
            # COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
            # stand-alone repository
            COMMAND ${GIT_EXECUTABLE} clone --recursive --progress ${repo_address} ${dst_dir}
            # WORKING_DIRECTORY ${dst_dir}
            RESULT_VARIABLE GIT_CLONE_RESULT
            OUTPUT_QUIET
            # log turn-off
            ERROR_QUIET
        )
        if(NOT GIT_CLONE_RESULT EQUAL "0")
            message(FATAL_ERROR "git clone --recursive ${repo_address} failed.")
        endif()
        # if((NOT EXISTS "${dst_dir}/CMakeLists.txt") OR (NOT EXISTS "${dst_dir}/host-runtime/CMakeLists.txt"))
        #     message(FATAL_ERROR "git clone --recursive ${repo_address} failed.")
        # endif()
    endif(GIT_FOUND)
endmacro(git_clone)

# git checkout commid_id
macro(git_checkout repo_dir commit_id)
    if(EXISTS "${repo_dir}/.git")
        execute_process(
            WORKING_DIRECTORY ${repo_dir}
            COMMAND ${GIT_EXECUTABLE} checkout ${commit_id}
            RESULT_VARIABLE GIT_CHECKOUT_RESULT
            OUTPUT_QUIET
            ERROR_QUIET
            OUTPUT_VARIABLE GIT_CHECKOUT_OUTPUT
        )

        if(NOT GIT_CHECKOUT_RESULT EQUAL "0")
            message(FATAL_ERROR "Git Checkout failed (${repo_dir}) : ${commit_id} ")
        endif()
        message(STATUS "Git Checkout (${repo_dir}) : ${commit_id} ")
    endif()
endmacro(git_checkout)

# git rev-parse --short HEAD
macro(get_git_hash repo_dir_name commit_id_target)
    find_package(Git QUIET)
    if(GIT_FOUND)
        execute_process(
            WORKING_DIRECTORY ${repo_dir_name}
            COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
	    OUTPUT_VARIABLE git_hash_short
	    OUTPUT_STRIP_TRAILING_WHITESPACE
            ERROR_QUIET
        )
        # message(STATUS "git_hash_short in func: ${git_hash_short}")

        if(NOT "${git_hash_short}" STREQUAL "${commit_id_target}")
            git_checkout(${repo_dir_name} ${commit_id_target})
        endif()
        message(STATUS "${repo_dir_name}.git Commit_ID (${commit_id_target}): True")

    endif(GIT_FOUND)
endmacro(get_git_hash)

# git clone main function
function(clone_repo repo_name commit_id enable_clone)
    set(repo_git "git@gitlab.tsingmicro.com:tx8_toolchain/${repo_name}.git")
    set(repo_https "http://gitlab.tsingmicro.com/tx8_toolchain/${repo_name}.git")
    set(repo_dir "${CMAKE_CURRENT_SOURCE_DIR}/third-party/${repo_name}")
    
    if(NOT enable_clone)
        set(enable_clone OFF)
    endif()

    if(${enable_clone})
        if(NOT EXISTS "${repo_dir}")
            file(MAKE_DIRECTORY ${repo_dir})
        endif()
        
        if(NOT EXISTS "${repo_dir}/.git")
            git_clone(${repo_git} ${repo_dir})
        endif()
        message(STATUS "Found ${repo_name}.git source code: True")
        
	# set(git_hash "")
        get_git_hash(${repo_dir} ${commit_id})

	#if(NOT "${git_hash}" STREQUAL "${commit_id}")
	#    git_checkout(${repo_dir} ${commit_id})
	#endif()
	#message(STATUS "${repo_name}.git Commit_ID (${commit_id}): True")
    endif()
endfunction()

# set(host_commmit_id cde27835)
# add_definitions(-DRUNTIME_GIT_COMMIT_ID="${host_commmit_id}")
message(STATUS "Using RUNTIME_GIT_COMMIT_ID: ${RUNTIME_GIT_COMMIT_ID}")
# --------     clone Host runtime repo to third-party if not exists    --------
option(enable_clone_host "tx8-host" OFF)
if (enable_clone_host)
    clone_repo(tx8-host ${host_commmit_id} ${enable_clone_host})
endif()
# --------     clone RT_Thread repo to third-party if not exists    --------
option(enable_clone_rtt "tx8-yoc-rt-thread-smp" OFF)
if (enable_clone_rtt)
clone_repo(tx8-yoc-rt-thread-smp 90dd2a05 ${enable_clone_rtt})
endif()
# --------     clone tx8be-oplib repo to third-party if not exists    --------
option(enable_clone_oplib "tx8be-oplib" OFF)
if (enable_clone_oplib)
    clone_repo(tx8be-oplib fb860f02 ${enable_clone_oplib})
endif()
# --------     clone tx8be-mlir repo to third-party if not exists    --------
# tx8be-mlir repo is too big to clone 
#################################################################################

# --------    tx8_runtime_lib solib     --------
set(HOST_RUNTIME_INCLUDE "")
set(HOST_RUNTIME_SRC_FILES "")

# set(HRT_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third-party/tx8-host/host-runtime)
# set(tx8_runtime_lib tx8_runtime)
# set(HOST_RUNTIME_INCLUDE    "${HRT_SRC_DIR}/include/extern/"
#                             "${HRT_SRC_DIR}/include/host/hardware/"
#                             "${HRT_SRC_DIR}/include/host/")

option(BUILD_HOSTRUNTIME "Build for Device" OFF)

# --------   Build tx8_runtime_lib solib     --------
# if(BUILD_HOSTRUNTIME)
#     message(STATUS "Build with Device(Tx81): ${BUILD_HOSTRUNTIME}")

#     find_package(PkgConfig REQUIRED)
#     if(NOT PKG_CONFIG_FOUND)
#         message(FATAL_ERROR "Found PkgConfig: ${PKG_CONFIG_FOUND}")
#     endif()

#     pkg_check_modules(kuiper_deps IMPORTED_TARGET libkuiper)
#     if(NOT kuiper_deps_FOUND)
#         message(FATAL_ERROR "Found libkuiper: ${kuiper_deps_FOUND}")
#     endif()

#     # --------   build jsoncpp solib     --------  
#     # add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/third-party/tx8-host/3rd_party/jsoncpp)
#     # include_directories(${CMAKE_CURRENT_SOURCE_DIR}/third-party/tx8-host/3rd_party/jsoncpp/include)

#     # --------   set Xuantie compiler    -------- 
#     # set(RTT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/third-party/tx8-yoc-rt-thread-smp")
#     # set(RISCV_GCC_TAG Xuantie-900-gcc-elf-newlib-x86_64-V2.8.0)
#     # set(RISCV_TOOLCHAIN_BIN ${RTT_DIR}/tool/${RISCV_GCC_TAG}/bin)

#     # set(RTT_RISCV_INCLUDE ${RTT_DIR}/boards/board_riscv_tx81/include
#     #                       ${RTT_DIR}/components/csi/csi2/include/core/cmsis
#     #                       ${RTT_DIR}/components/oplib_tx81/riscv/ap/include
#     #                       ${RTT_DIR}/components/oplib_tx81/riscv/common/include
#     #                       ${RTT_DIR}/components/oplib_tx81/riscv/host/include
#     #                       ${RTT_DIR}/components/oplib_tx81/riscv/kcore/include
#     #                       ${RTT_DIR}/components/oplib_tx81/riscv/riscv/include
#     #                       )
#     # set(RTT_RISCV_SRC
#     #                       ${RTT_DIR}/components/chip_riscv_c908_series
#     #                       )

#     # set(RTT_RISCV_SRC_DIR ${RTT_DIR}/components/oplib_tx81/riscv/riscv/src)

#     # add_custom_command(
#     #     OUTPUT ${RTT_DIR}/tool/${RISCV_GCC_TAG}
#     #     COMMAND tar xf ${RISCV_GCC_TAG}.tar.gz
#     #     WORKING_DIRECTORY ${RTT_DIR}/tool
#     # )

#     # add_custom_target(set_rtt ALL
#     #     COMMAND export PATH=${RISCV_TOOLCHAIN_BIN}:$$PATH
#     #     WORKING_DIRECTORY ${RTT_DIR}/tool
#     #     DEPENDS ${RTT_DIR}/tool/${RISCV_GCC_TAG}
#     # )

#     execute_process(
#         COMMAND pkg-config --cflags libkuiper
#         OUTPUT_VARIABLE DPDK_CONFIG_CFLAGS
#         RESULT_VARIABLE PKG_CONFIG_RESULT
#         OUTPUT_STRIP_TRAILING_WHITESPACE
#     )

#     if(NOT PKG_CONFIG_RESULT EQUAL "0")
#         message(FATAL_ERROR "pkg-config --cflags for libkuiper failed with result: ${PKG_CONFIG_RESULT}")
#     endif()

#     execute_process(
#         COMMAND pkg-config --libs libkuiper
#         OUTPUT_VARIABLE DPDK_CONFIG_LIBS
#         RESULT_VARIABLE PKG_CONFIG_RESULT
#         OUTPUT_STRIP_TRAILING_WHITESPACE
#     )

#     if(NOT PKG_CONFIG_RESULT EQUAL "0")
#         message(FATAL_ERROR "pkg-config --libs for libkuiper failed with result: ${PKG_CONFIG_RESULT}")
#     endif ()

#     string(STRIP "${DPDK_CONFIG_CFLAGS}" DPDK_CONFIG_CFLAGS)
#     set(HOST_COMPILE_FLAGS -std=c++17)
#     set(HOST_COMPILE_FLAGS ${HOST_COMPILE_FLAGS} ${DPDK_CONFIG_CFLAGS})
#     message(STATUS "Host_Runtime compile flags: ${HOST_COMPILE_FLAGS}")
#     message(STATUS "Host_Runtime compile libs: ${DPDK_CONFIG_LIBS}")




#     # --------   build tx8_runtime_lib solib     -------- 
#     file(GLOB_RECURSE  HOST_RUNTIME_SRC_FILES "${HRT_SRC_DIR}/src/host/*.cpp")

#     add_library(${tx8_runtime_lib} SHARED ${HOST_RUNTIME_SRC_FILES})
#     target_include_directories(${tx8_runtime_lib} PUBLIC ${HOST_RUNTIME_INCLUDE})
#     target_compile_options(${tx8_runtime_lib} PUBLIC ${HOST_COMPILE_FLAGS})
#     target_compile_options(${tx8_runtime_lib} PUBLIC -fPIC)
#     target_compile_definitions(${tx8_runtime_lib} PUBLIC "USING_RISCV")
#     target_link_libraries(${tx8_runtime_lib} PUBLIC
#                             ${DPDK_CONFIG_LIBS}
#                             PkgConfig::kuiper_deps
#                             pthread
#                             rt
#                             jsoncpp # locate in soc_docker : /lib/x86_64-linux-gnu
#                             )
#     set_target_properties(${tx8_runtime_lib} 
#                           PROPERTIES 
#                           # OUTPUT_NAME ${LIB_NAME} 
#                           LIBRARY_OUTPUT_DIRECTORY ${output_lib_dir}
#                          )

# endif(BUILD_HOSTRUNTIME)

# # --------   link tx8_runtime_lib solib     --------
# set(hrt_so ${CMAKE_CURRENT_BINARY_DIR}/libtx8_runtime.so)
# set(origin_hrt_so ${HRT_SRC_DIR}/output/lib/libtx8_runtime.so)

# if(NOT EXISTS "${origin_hrt_so}")
#     execute_process(
#         COMMAND bash build.sh
#         WORKING_DIRECTORY ${HRT_SRC_DIR}
#         OUTPUT_VARIABLE BUILD_HOST_RUNTIME_OUT
#         ERROR_VARIABLE BUILD_HOST_RUNTIME_ERROR_MSG
#         RESULT_VARIABLE BUILD_HOST_RUNTIME_RES
#     )

#     # message(STATUS "output: ${BUILD_HOST_RUNTIME_OUT}")
#     if(NOT BUILD_HOST_RUNTIME_RES EQUAL "0")
#         message(FATAL_ERROR "build host runtime error: ${BUILD_HOST_RUNTIME_ERROR_MSG}")
#     endif ()
# endif ()

# if(NOT BUILD_HOSTRUNTIME)
#     if(EXISTS ${origin_hrt_so})
#         message(STATUS "Found ${origin_hrt_so} : True")
#     else()
#         message(FATAL_ERROR "Found ${origin_hrt_so} : False")
#     endif()
# endif()

# if(NOT EXISTS "${hrt_so}")
#     execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different
#         ${origin_hrt_so}
#         ${hrt_so}
#     )
# endif ()

# link_directories(${HRT_SRC_DIR}/output/lib)

# --------   set backend kernel: Txdnn_Backend_MainC Kernels   --------
option(TXDNN_BACKEND_MAINC "using backend kernel main.c" ON)

# # --------   find_package: pybind11 from header-only source code    --------
# set(pybind11_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third-party/pybind11/share/cmake/pybind11)
# list(APPEND CMAKE_MODULE_PATH ${pybind11_DIR})
# find_package(Python COMPONENTS Interpreter Development)
# find_package(pybind11 CONFIG)

# # --------   include: nolmann/json from header-only source code    --------
# set(nlohmann_json_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third-party)

# --------   Set, Build and Link      --------
# include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src)
# add_subdirectory(src/utils)
# add_subdirectory(csrc/src/txdart)

set(TXDNN_INCLUDE include)
set(TXDNN_SRC_INCLUDE src)
# include_directories(${HOST_RUNTIME_INCLUDE} ${TXDNN_INCLUDE} ${TXDNN_SRC_INCLUDE})
include_directories(${TXDNN_INCLUDE} ${TXDNN_SRC_INCLUDE})


# --------   Build txdnn CPP so      --------
set(txdnn_lib txdnn)
file(GLOB_RECURSE TXDNN_SOURCE_FILES    src/txdnn_entry.cpp
                                        src/txdnn_c_entry/layers/rope.cpp
                                        src/tensor/tensor_descriptor.cpp
                                        src/tensor/seq_tensor.cpp
                                        src/layers/rope/rope.cpp
                                        src/backend/program/program_impl_mainc_group.cpp
                                        src/backend/program/program_mainc_group.cpp
                                        src/backend/kernel_interface/kernel_scheduler_inc.cpp
                                        src/backend/kernel_interface/kernel_scheduler_src.cpp
                                        src/backend/intrinsic/handle_intrinsic.cpp
                                        src/backend/device/txda_device.cpp
                                        src/utils/filesystem/file_manipulate.cpp
)

add_library(${txdnn_lib} SHARED ${TXDNN_SOURCE_FILES})
target_compile_options(${txdnn_lib} PRIVATE ${GLOBAL_OPTIONS})
# target_compile_definitions(${txdnn_lib} PRIVATE TXDA_BUILD_LIB)
target_compile_definitions(${txdnn_lib} PRIVATE "CMAKE_EXPORT_COMPILE_COMMANDS=ON")
target_include_directories(${txdnn_lib} PRIVATE ${nlohmann_json_DIR})
# target_link_libraries(${txdnn_lib} PRIVATE ${GLOBAL_LINKS} ${tx8_runtime_lib})
set_target_properties(${txdnn_lib}
                        PROPERTIES
                        BUILD_WITH_INSTALL_RPATH ON
                        INSTALL_RPATH "\$ORIGIN"
                      )
install(TARGETS ${txdnn_lib} LIBRARY DESTINATION ${output_lib_dir})
install(FILES ${hrt_so} DESTINATION ${output_lib_dir})

# --------   Build CPP Examples      --------
option(BUILD_EXAMPLES "Build for CPP examples" OFF)
if(BUILD_EXAMPLES)    
    add_subdirectory(examples)
endif(BUILD_EXAMPLES)


# --------   Build Python so      --------
option(PYBIND_LIB "Build for PybindLib" OFF)
file(GLOB_RECURSE txdnn_binding_src    csrc/bind_txdnn.cpp
                                        csrc/txda_bindings.cpp  )
set(LIB_NAME txdnn)
if(PYBIND_LIB)
    pybind11_add_module(${LIB_NAME} ${txdart_binding_src})
    target_compile_options(${LIB_NAME} PRIVATE ${GLOBAL_OPTIONS})
    target_compile_definitions(${LIB_NAME} PRIVATE TXDA_BUILD_LIB)
    target_include_directories(${LIB_NAME} PRIVATE ${nlohmann_json_DIR})
    target_link_libraries(${LIB_NAME} PRIVATE ${GLOBAL_LINKS} pybind11::module Python::Python ${txdnn_lib} ${txdaccl_lib})
    set_target_properties(${LIB_NAME} 
                            PROPERTIES 
                            BUILD_WITH_INSTALL_RPATH ON
                            INSTALL_RPATH "\$ORIGIN/lib:\$ORIGIN/../..:\$ORIGIN/../../.."
                        )
    install(TARGETS ${LIB_NAME} LIBRARY DESTINATION ${base_out_dir})
endif(PYBIND_LIB)